using System;
using System.Configuration;
using Castle.Core.Internal;
using Castle.MicroKernel.Facilities;
using Castle.MicroKernel.Registration;
using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Conventions.Helpers;
using LaxCraft.Domain;
using NHibernate;
using NHibernate.ByteCode.Castle;
using NHibernate.Tool.hbm2ddl;
using Configuration = NHibernate.Cfg.Configuration;

namespace LaxCraft.Plumbing
{
	public class PersistenceFacility : AbstractFacility
	{
		protected override void Init()
		{
			var config = BuildDatabaseConfiguration();

			Kernel.Register(
				Component.For<ISessionFactory>()
					.UsingFactoryMethod(config.BuildSessionFactory),
				Component.For<ISession>()
					.UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
					.LifeStyle.PerWebRequest);
		}

		private Configuration BuildDatabaseConfiguration()
		{
			return Fluently.Configure()
				.Database(SetupDatabase)
				.Mappings(m => m.AutoMappings.Add(CreateMappingModel()))
				.ExposeConfiguration(ConfigurePersistence)
				.BuildConfiguration();
		}


		protected virtual AutoPersistenceModel CreateMappingModel()
		{
			var m = AutoMap.Assembly(typeof (EntityBase).Assembly)
				.Where(IsDomainEntity)
				.OverrideAll(ShouldIgnoreProperty)
                .Conventions.Add( DefaultCascade.All())
				.IgnoreBase<EntityBase>();

			return m;
		}

		protected virtual IPersistenceConfigurer SetupDatabase()
		{
			return MsSqlConfiguration.MsSql2008
				.UseOuterJoin()
				.ProxyFactoryFactory(typeof (ProxyFactoryFactory))
				.ConnectionString(x => x.FromConnectionStringWithKey("ApplicationServices"))
				.ShowSql();
		}

		protected virtual void ConfigurePersistence(Configuration config)
		{
			SchemaMetadataUpdater.QuoteTableAndColumns(config);

		    switch (ConfigurationManager.AppSettings["SchemaUpdate"])
		    {
		        case "Update":
                    new SchemaUpdate(config).Execute(false, true);
                    break;
                case "Build":
		            var exportUtil = new SchemaExport(config);
                    exportUtil.Drop(false, true);

                    exportUtil.Create(false, true);
		            break;
		    }

		    

		}

		protected virtual bool IsDomainEntity(Type t)
		{
			return typeof (EntityBase).IsAssignableFrom(t);
		}

		private void ShouldIgnoreProperty(IPropertyIgnorer property)
		{
			property.IgnoreProperties(p => p.MemberInfo.HasAttribute<DoNotMapAttribute>());
		}
	}
}